﻿using System.Net;

namespace Sage.Http.Content
{
    /// <summary>
    /// 支持进度报告的HTTP内容类
    /// </summary>
    public class ProgressableStreamContent : HttpContent
    {
        private const int defaultBufferSize = 4096;
        private readonly HttpContent content;
        private readonly IProgress<float> progress;
        private readonly int bufferSize;
        private readonly CancellationToken cancellationToken;
        /// <summary>
        /// 初始化支持进度报告的HTTP内容实例
        /// </summary>
        /// <param name="content">原始HTTP内容</param>
        /// <param name="progress">进度报告接口</param>
        /// <param name="bufferSize">缓冲区大小</param>
        /// <param name="cancellationToken">取消令牌</param>
        public ProgressableStreamContent(
            HttpContent content,
            IProgress<float> progress,
            int bufferSize = defaultBufferSize,
            CancellationToken cancellationToken = default)
        {
            this.content = content ?? throw new ArgumentNullException(nameof(content));
            this.progress = progress ?? throw new ArgumentNullException(nameof(progress));
            this.bufferSize = bufferSize;
            this.cancellationToken = cancellationToken;

            // 复制内容头
            foreach (var header in content.Headers)
            {
                Headers.Add(header.Key, header.Value);
            }
        }

        /// <summary>
        /// 将内容序列化到流，同时报告进度
        /// </summary>
        /// <param name="stream">目标流</param>
        /// <param name="context">传输上下文</param>
        protected override async Task SerializeToStreamAsync(Stream stream, TransportContext? context)
        {
            var buffer = new byte[bufferSize];
            TryComputeLength(out long size);
            var uploaded = 0L;

            using var contentStream = await content.ReadAsStreamAsync(cancellationToken);
            int read;
            while ((read = await contentStream.ReadAsync(buffer, cancellationToken)) > 0)
            {
                await stream.WriteAsync(buffer.AsMemory(0, read), cancellationToken);
                uploaded += read;
                float percentage = size > 0 ? (float)uploaded / size : -1;
                progress.Report(percentage);

                // 检查取消令牌，以便尽快退出
                cancellationToken.ThrowIfCancellationRequested();
            }
        }

        /// <summary>
        /// 尝试计算内容长度
        /// </summary>
        /// <param name="length">输出长度</param>
        /// <returns>是否成功计算长度</returns>
        protected override bool TryComputeLength(out long length)
        {
            if (content.Headers.ContentLength.HasValue)
            {
                length = content.Headers.ContentLength.Value;
                return true;
            }
            length = -1;
            return false;
        }

        /// <summary>
        /// 重写同步 Dispose 方法
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                content?.Dispose();
            }
            base.Dispose(disposing);
        }

    }
}
